#ifndef HELMETADC_H
#define HELMETADC_H

#include "HelmetGlobals.h"
#include "HelmetSystemConfig.h"

/**************************************************************************************
	Global Variables
**************************************************************************************/
unsigned int rawtemp, rawx=0, rawy=0, rawz=0; // Raw ADC data
unsigned char ADCBufferPos = 0;

float AccelSamples[10] = {0,0,0,0,0,0,0,0,0,0}; // buffer of 10 ADC conversions
float ThermSamples[10] = {0,0,0,0,0,0,0,0,0,0}; // buffer of 10 thermometer conversions

/**************************************************************************************
	Definitions
**************************************************************************************/
#define ACCEL_X BIT_12 // Accelerometer X input
#define ACCEL_Y BIT_13 // 	" 		  Y input
#define ACCEL_Z BIT_14 //	"		  Z input
//#define PORTB_ADC_MASK ( ACCEL_X | ACCEL_Y | ACCEL_Z )
#define ADCSingleConversionSequenceModeEnable() AD1CON1SET=0x0010 // sets CLRASAM bit (stop sampling after auto-sample)
#define ADCSingleConversionSequenceModeDisable() AD1CON1CLR=0x0010 // clears CLRASAM bit (continuous auto-sampling mode)
#define StartADC() AD1CON1SET=0x0004 // sets ASAM bit (start auto-sampling)
#define StopADC() AD1CON1CLR=0x0004 // clears ASAM bit (stop auto-sampling)
#define IncrementADCBufferPos() if(ADCBufferPos==9) ADCBufferPos=0; else ADCBufferPos++


/**************************************************************************************
	Function Prototypes
**************************************************************************************/
void ConfigADC( void );
void BufferAccelerationTemperature( float accel, float temp );

/**************************************************************************************
	Functions
**************************************************************************************/
void ConfigADC() {
	
	CloseADC10();		// ensure the ADC is off before setting the configuration
	
	// 					Turn ADC on   | stop in idle  | ouput in int16    | auto-convert | disable autosample    | sampling off
	#define ADCCONFIG1  ADC_MODULE_ON | ADC_IDLE_STOP | ADC_FORMAT_INTG16 | ADC_CLK_AUTO | ADC_AUTO_SAMPLING_OFF | ADC_SAMP_OFF
	
	// 					ADC ref Vdd/Vss    | disable offset test    | scan mode on | perform 4 samples     | alternate buffers off | alternate inputs off
	#define ADCCONFIG2  ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_ON  | ADC_SAMPLES_PER_INT_4 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_OFF
	
	//					sampling time      | use periph clock    | Tad=Tpb*2
	#define ADCCONFIG3  ADC_SAMPLE_TIME_15 | ADC_CONV_CLK_SYSTEM | ADC_CONV_CLK_Tcy2
	
	//              	set AN9, AN12, 13, and 14 as analog inputs
	#define ADCCONFIG4	ENABLE_AN9_ANA | ENABLE_AN12_ANA | ENABLE_AN13_ANA | ENABLE_AN14_ANA
	
	//					xor with 0xFFFF is a bit-wise inversion
	#define INVERT		0xFFFF^
	
	//					do not skip scan of AN9, AN12, 13, and 14
	#define ADCCONFIG5	INVERT( SKIP_SCAN_AN9 | SKIP_SCAN_AN12 | SKIP_SCAN_AN13 | SKIP_SCAN_AN14 )
	
	//         configure ADC using the parameters defined above
	OpenADC10( ADCCONFIG1, ADCCONFIG2, ADCCONFIG3, ADCCONFIG4, ADCCONFIG5 );
	
	// auto-sample will not sample continuously after an interrupt
	ADCSingleConversionSequenceModeEnable();
	
	// enable ADC interrupt - priority 1
	ConfigIntADC10( ADC_INT_ON | ADC_INT_PRI_2 | ADC_INT_SUB_PRI_0 );
	
	// Enable the ADC
	EnableADC10();	
}

float ConvertADCData() {
	float actx = ( rawx - 512.225 ) / 63.95;
	float acty = ( rawy - 524.15 ) / 64.25;
	float actz = ( rawz - 498.00 ) / 62.7;
	float gmag = sqrt( actx*actx + acty*acty + actz*actz ); // magnitude of g's experienced
	float temp = rawtemp*(20.0/859.0);
	BufferAccelerationTemperature( gmag, temp );
	return gmag;
}

void BufferAccelerationTemperature( float accel, float temp ) {
	AccelSamples[ADCBufferPos]=accel;
	ThermSamples[ADCBufferPos]=temp;
	IncrementADCBufferPos();
}	

float GetAverageAcceleration() {
	float avg = 0;
	float sum = 0;
	unsigned short int i;
	for( i=0 ; i<10 ; i++ ) {
		sum = sum + AccelSamples[i];	
	}
	avg = sum/10;
	return avg;
}

float GetAverageTemperature() {
	float avg = 0;
	float sum = 0;
	unsigned short int i;
	for( i=0 ; i<10 ; i++ ) {
		sum = sum + ThermSamples[i];	
	}
	avg = sum/10;
	return avg;
}	

/**************************************************************************************
	Interrupt handling routines
**************************************************************************************/
// Note: Priority 1 (lowest) to 7 (highest)

void __ISR(_ADC_VECTOR, ipl2) AdcHandler(void) 
{ 
	unsigned int offset;	
	
	// Get buffer location and make offset
	offset = 8 * ((ReadActiveBufferADC10() & 0x01));
	
	// Read buffer locations
	rawtemp = ReadADC10(offset++);
	rawx = ReadADC10(offset++);
	rawy = ReadADC10(offset++);
	rawz = ReadADC10(offset);
	
	// Flag ADCDone
	DoInMain( ConvertADC );
	
	// Clear interrupt flag
	INTClearFlag( INT_AD1 );
}

/*	
	*CALCULATION NOTE*

	Data was collected using the average of a number of
		X, Y, and Z points placed approximately in such
		a position to give 0, -1, and 1 g measurements
		for each.  Four 0 g measurements for each axis
		were acquired and 1 measurement each of -1 and 
		1 g. The zero-position measurements were 
		averaged to achieve the data below.  The 
		average difference between zero and +/-1 is 
		given as delta. The overall average delta is
		given afterwards. This should give a good
		approximate measurement of g-force overall but
		will not be perfectly accurate in all settings.

	Data collection results:
		X(0) = 512.225 ; delta-x = 63.95
		Y(0) = 524.15 ; delta-y = 64.25
		Z(0) = 495.45 ; delta-z = 62.7 
		Delta avg = 63.633
		
	Now create a linear model based upon the data:
		R = raw measurement
		O = offset ( X(0), Y(0), Z(0) )
		Z = ( R - O ) = zero-ed data (offset removed)
		D = change in measurement per g (delta-x, etc)
		A = Z / D = calculated actual measurement
		
	These have been experimentally tested within lab
		conditions to be at maximum +/-0.1 g off from
		actual expected measurements. Based upon a
		whitepaper entitled "Accelerometer Calibration
		and Dynamic Bias and Gravity Estimation:
		Analysis, Design, and Experimental Evaluation,"
		it is likely the accelerometer offset bias can
		be expected to deviate by maximally 0.04 g's.
		As this product does not require such precise
		measurements, the +/-0.1 g measurement can be
		expected to remain within this range.											
*/

#endif